#include "geometry.h"

#include <cfloat>
#include <cmath>
#include <cassert>
#include <stdlib.h>
#include <time.h>
#include "Vector.h"

bool intersectSegment(const Vector& v0, const Vector& v1, const Vector& v2, const Vector& v3)
{

	double c2 = (v2-v0).Cross(v2-v1);
	double c3 = (v3-v0).Cross(v3-v1);
	double c0 = (v0-v2).Cross(v0-v3);
	double c1 = (v1-v2).Cross(v1-v3);
	if((c0>0&&c1>0) || (c0<0&&c1<0)) return false;
	if((c2>0&&c3>0) || (c2<0&&c3<0)) return false;
	return true;
}


//edited by lqw
Site::Site(double x, double y)
{
	this->p0_.x = x;
	this->p0_.y = y;
}

int Site::isAboveOf(const Site& site) const
{
	if(site.isPoint_)
	{
		if(equalDouble(p0_.y, site.p0_.y))
			return 0;
		else if( p0_.y > site.p0_.y)
			return 1;
		else
			return -1;
	}else{
		double c = (site.p0_-p0_).Cross(site.p1_-p0_);
		if(equalDouble(c, 0.0))
			return 0;
		else if(c > 0.0)
			return 1;
		else
			return -1;
	}
}

void Graph::setSize(double w, double h)
{
	width_ = w;
	height_ = h;
}

bool Graph::addPoint(const Vector &point )
{
	std::vector<Site>::iterator it = sites_.begin();
	std::vector<Site>::iterator end = sites_.end();
	for(; it!=end; ++it)
	{
		if(it->isPoint_ && it->p0_ == point)
		{
			return false;
		}
		if(!it->isPoint_ && equalDouble((point-it->p0_).Cross(point-it->p1_), 0.0))
		{
			return false;
		}
	}
	sites_.push_back(Site(sites_.size(), point));
	return true;
}

bool Graph::addSegment(Vector p0, Vector p1)
{
	std::vector<Site>::iterator it = sites_.begin();
	std::vector<Site>::iterator end = sites_.end();
	for(; it!=end; ++it)
	{
		if(it->isPoint_ && !(it->p0_==p0 || it->p0_==p1) && equalDouble((it->p0_-p0).Cross(it->p0_-p1), 0.0))
		{
			return false;
		}
		if(!it->isPoint_ && intersectSegment(p0, p1, it->p0_, it->p1_))
		{
			return false;
		}
	}
	if(equalDouble(p0.x, p1.x))
	{
		if(p0.y > p1.y)
		{
			p0.y -= (PRECISION/2);
			sites_.push_back(Site(sites_.size(), p0, p1));
		}else if(p0.y < p1.y)
		{
			p1.y -= (PRECISION/2);
			sites_.push_back(Site(sites_.size(), p1, p0));
		}
	}else if(p0.x < p1.x)
	{
		double len = (p1-p0).getLength();
		Vector pb = (p0*(len-PRECISION/2) + p1*PRECISION/2)/len;
		sites_.push_back(Site(sites_.size(), pb, p1));
	}else{
		double len = (p0-p1).getLength();
		Vector pb = (p1*(len-PRECISION/2) + p0*PRECISION/2)/len;
		sites_.push_back(Site(sites_.size(), pb, p0));
	}
	return true;
}


//add random points use the rand seed given
void Graph::addRandomPoints(int count, unsigned int p_iRandSeed)
{
	srand(p_iRandSeed);

	double wfactor = width_ / RAND_MAX;
	double hfactor = height_ / RAND_MAX;

	sites_.clear();
	sites_.reserve(count);
	for (int i = 0; i < count; i++)
	{
		Vector newPoint( 
			(double)(rand() * wfactor) ,
			(double)(rand() * hfactor));

		bool isNew = true;
		for (int j = (int)sites_.size() - 1; j >= 0; j--)
		{
			if (equalDouble(sites_[j].p0_.x, newPoint.x) && equalDouble(sites_[j].p0_.y, newPoint.y))
			{
				isNew = false;
				break;
			}
		}
		if (isNew)
			addPoint(newPoint);
	}
}

// add random points
void Graph::addRandomPoints(int count)
{
	srand((unsigned int)time(NULL));

	double wfactor = width_ / RAND_MAX;
	double hfactor = height_ / RAND_MAX;

	sites_.clear();
	sites_.reserve(count);
	for (int i = 0; i < count; i++)
	{
		Vector newPoint( 
			(double)(rand() * wfactor) ,
			(double)(rand() * hfactor));

		bool isNew = true;
		for (int j = (int)sites_.size() - 1; j >= 0; j--)
		{
			if (equalDouble(sites_[j].p0_.x, newPoint.x) && equalDouble(sites_[j].p0_.y, newPoint.y))
			{
				isNew = false;
				break;
			}
		}
		if (isNew)
			addPoint(newPoint);
	}
}

int Graph::getSiteCount() const
{
	return sites_.size();
}

Site Graph::getSite(int id) const
{
	return sites_[id];
}

std::vector<Site>& Graph::getSites()
{
	return sites_;
}

